#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
定时任务执行器
用于定期执行定时任务，如发送周报邮件等
"""

import os
import sys
import logging
from datetime import datetime, timedelta
from dotenv import load_dotenv

# 添加项目路径到系统路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

# 加载环境变量
load_dotenv()

from models import db, ScheduledTask
from utils import send_weekly_report_email
from app import create_app

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

def execute_scheduled_tasks():
    """执行所有需要执行的定时任务"""
    logger.info("开始执行定时任务...")
    
    # 创建应用上下文
    app = create_app()
    
    with app.app_context():
        # 获取所有启用的定时任务
        tasks = ScheduledTask.query.filter_by(is_active=True).all()
        logger.info(f"找到 {len(tasks)} 个启用的定时任务")
        
        now = datetime.now()
        executed_count = 0
        
        for task in tasks:
            try:
                # 检查是否应该执行此任务
                if should_execute_task(task, now):
                    logger.info(f"执行定时任务 ID: {task.id}, 用户ID: {task.user_id}")
                    
                    # 根据任务频率执行相应操作
                    if execute_task(task, now):
                        # 更新上次执行时间
                        task.last_run = now
                        db.session.commit()
                        executed_count += 1
                        logger.info(f"定时任务 ID: {task.id} 执行成功")
                    else:
                        logger.error(f"定时任务 ID: {task.id} 执行失败")
            except Exception as e:
                logger.error(f"执行定时任务 ID: {task.id} 时出错: {str(e)}")
                # 继续执行其他任务
        
        logger.info(f"定时任务执行完成，共执行 {executed_count} 个任务")

def should_execute_task(task, now):
    """判断是否应该执行某个任务"""
    # 如果从未执行过，检查是否到了执行时间
    if task.last_run is None:
        return is_time_to_execute(task, now)
    
    # 如果已经执行过，检查是否到了下一个执行周期
    if task.frequency == 'daily':
        # 每日任务，检查是否是第二天且到了执行时间
        next_run_time = datetime(
            now.year, now.month, now.day,
            task.hour, task.minute
        )
        # 如果今天还没到执行时间，则下次执行时间是今天
        # 如果今天已经过了执行时间，则下次执行时间是明天
        if now.hour < task.hour or (now.hour == task.hour and now.minute < task.minute):
            next_run_time = datetime(
                now.year, now.month, now.day,
                task.hour, task.minute
            )
        else:
            next_run_time = datetime(
                now.year, now.month, now.day,
                task.hour, task.minute
            ) + timedelta(days=1)
            
        return now >= next_run_time and task.last_run < next_run_time
        
    elif task.frequency == 'weekly':
        # 每周任务，检查是否是下一周且到了执行时间
        # 计算下次执行时间
        days_ahead = task.day_of_week - now.weekday()
        if days_ahead <= 0:  # Target day already happened this week
            days_ahead += 7
        next_run_date = now.date() + timedelta(days_ahead)
        next_run_time = datetime(
            next_run_date.year, next_run_date.month, next_run_date.day,
            task.hour, task.minute
        )
        
        # 如果本周还没到执行时间，则下次执行时间是本周
        # 如果本周已经过了执行时间，则下次执行时间是下周
        this_week_target = datetime(
            now.year, now.month, now.day,
            task.hour, task.minute
        ) + timedelta(days=(task.day_of_week - now.weekday()))
        
        if this_week_target.date() >= now.date():
            next_run_time = this_week_target
        else:
            next_run_time = this_week_target + timedelta(days=7)
            
        return now >= next_run_time and task.last_run < next_run_time
        
    elif task.frequency == 'monthly':
        # 每月任务，检查是否是下一月且到了执行时间
        next_run_time = calculate_next_monthly_run_time(task, now)
        return now >= next_run_time and task.last_run < next_run_time
    
    return False

def is_time_to_execute(task, now):
    """判断是否到了执行时间"""
    if task.frequency == 'daily':
        # 每日任务，检查当前时间是否匹配
        return now.hour == task.hour and now.minute == task.minute
        
    elif task.frequency == 'weekly':
        # 每周任务，检查星期几和时间是否匹配
        return (now.weekday() == task.day_of_week and 
                now.hour == task.hour and 
                now.minute == task.minute)
        
    elif task.frequency == 'monthly':
        # 每月任务，检查日期和时间是否匹配
        return (now.day == task.day_of_month and 
                now.hour == task.hour and 
                now.minute == task.minute)
    
    return False

def calculate_next_monthly_run_time(task, now):
    """计算下次月度任务执行时间"""
    try:
        next_run_time = datetime(
            now.year, now.month, task.day_of_month,
            task.hour, task.minute
        )
        # 如果本月的指定日期已经过了，则下次执行是下个月
        if next_run_time.date() < now.date():
            if now.month == 12:
                next_run_time = datetime(
                    now.year + 1, 1, task.day_of_month,
                    task.hour, task.minute
                )
            else:
                next_run_time = datetime(
                    now.year, now.month + 1, task.day_of_month,
                    task.hour, task.minute
                )
    except ValueError:
        # 如果指定的日期在该月不存在（如2月30日），则调整到月末
        if now.month == 12:
            next_run_time = datetime(
                now.year + 1, 1, 1,
                task.hour, task.minute
            ) - timedelta(days=1)
        else:
            next_run_time = datetime(
                now.year, now.month + 1, 1,
                task.hour, task.minute
            ) - timedelta(days=1)
    
    return next_run_time

def execute_task(task, now):
    """执行具体的任务"""
    try:
        if task.frequency == 'daily':
            # 每日报告：发送前一天的消费详情
            end_date = now.date() - timedelta(days=1)
            start_date = end_date
            return send_weekly_report_email(task.email, task.user_id, start_date, end_date)
            
        elif task.frequency == 'weekly':
            # 每周报告：发送上周的消费详情
            end_date = now.date() - timedelta(days=now.weekday() + 1)  # 上周日
            start_date = end_date - timedelta(days=6)  # 上周一
            return send_weekly_report_email(task.email, task.user_id, start_date, end_date)
            
        elif task.frequency == 'monthly':
            # 每月报告：发送上月的消费详情
            # 计算上月的第一天和最后一天
            if now.month == 1:
                start_date = datetime(now.year - 1, 12, 1).date()
                end_date = datetime(now.year - 1, 12, 31).date()
            else:
                start_date = datetime(now.year, now.month - 1, 1).date()
                # 计算上月最后一天
                if now.month == 2:
                    end_date = datetime(now.year, 1, 28).date()
                    # 检查是否是闰年
                    if now.year % 4 == 0 and (now.year % 100 != 0 or now.year % 400 == 0):
                        end_date = datetime(now.year, 1, 29).date()
                elif now.month in [4, 6, 9, 11]:
                    end_date = datetime(now.year, now.month - 1, 30).date()
                else:
                    end_date = datetime(now.year, now.month - 1, 31).date()
            return send_weekly_report_email(task.email, task.user_id, start_date, end_date)
        
        return False
    except Exception as e:
        logger.error(f"执行任务时出错: {str(e)}")
        return False

if __name__ == '__main__':
    execute_scheduled_tasks()